EBAZ4205 第七个工程 用zynq的PS资源驱动SPI彩色LCD(硬件SPI方案 紫色板子进,黑色板子请看第十三个工程) – 你好,FPGA

您所在的位置:网站首页 zynq spi用例 EBAZ4205 第七个工程 用zynq的PS资源驱动SPI彩色LCD(硬件SPI方案 紫色板子进,黑色板子请看第十三个工程) – 你好,FPGA

EBAZ4205 第七个工程 用zynq的PS资源驱动SPI彩色LCD(硬件SPI方案 紫色板子进,黑色板子请看第十三个工程) – 你好,FPGA

2023-11-06 17:28| 来源: 网络整理| 查看: 265

转接板上自带了一块LCD 屏幕,受IO资源限制,屏幕的接口使用了占用线序比较少的SPI接口,本文将通过ZYNQ的PS资源去点亮这个液晶屏幕

之前ZYNQ和紫色板子硬件SPI 存在的兼容性问题已经解决,以下工程可以直接调试使用 20220504

由于扩展板上的CS脚并未引出(屏幕厂家之前的demo没有引出) ,导致和ZYNQ 硬件SPI有些冲突,长时间待机SPI状态下, 屏幕会出现死机不能操作的情况,怀疑是和ZYNQ 释放了SPI总线有关,所以后面点亮屏幕改成用IO模拟的方式去点亮屏幕, 具体内容可以看第十个工程http://www.hellofpga.com/index.php/2022/03/08/ps_lcd_io_spi/

另外网站里还提供了GPIO模拟SPI点屏的例程序,在第十个工程

1.硬件介绍

转接板部分硬件LCD 接口如下图所示

BL 负责控制背光,高:屏幕点亮, 低:屏幕熄灭

SCL 时钟

SDA 数据

D/C 数据/命令指示

RES 屏幕复位

2.驱动思路

屏幕本身是连接到ZYNQ的PL资源上,点亮屏幕可以用纯fpga去驱动SPI的方式点亮,也可以用PS资源去点亮,为了方便演示,这里采用PS资源去实现

通过EMIO的方式 将PS部分硬件SPI资源映射到 ZYNQ的PL口上去点亮屏幕

3.创建工程

1)新建一个项目,芯片型号选择 XC7Z010CLG400-1

2) 创建一个BLOCK设计,并添加ZYNQ7 PROCESSING SYSTEM模块,软件自动生成了一个 zynq的block 如下图所示,接下来要做一些相应的设置,双击下图中的ZYNQ核

3)在ZYNQ中设置时钟功能:

找到 设置项目中的 Clock Configuration 选项, 在PL Fabric Clocks 设置自己需要的时钟频率,这里一共有4种频率可以设置 类似于我们的PLL功能。这里我们设置50M时钟

4)在zynq中设置DDR功能:

依次在弹窗里找到DDR Configuration→DDR Controller Configuration→DDR3,在Memory Part下拉菜单中根据自己板子上的DDR来选择相应的DDR3,本实验所用到型号:MT41K128M16JT 125,数据位宽选择16bit 最后点击“OK”,如下图所示。

5)在ZYNQ中配置SPI功能

因为屏幕的接口是连接到ZYNQ的PL口上,所以这里我们调用EMIO的SPI功能(即使用PS的SPI功能,但是内部连接映射到PL的GPIO口上)

 MIO Configuration→SPI 0勾上 并选择EMIO

6)由于屏幕的驱动除了SPI外,还需要3个额外的GPIO口来分别控制背光BL ,复位RES和 D/C信号,所以这里增加三个GPIO口,这里GPIO资源选择EMIO GPIO 位宽Width选择3(因为是3个IO口)

5)完成上述操作后, 点击“Run Block Automation”如下图所示。在弹出的选项中保持默认,点击“OK”,即可完成对ZYNQ7 Processing System的配置,并用鼠标连接FCLK_CLK和 M_AXI_GP0_ACLK,得到下图

在上图中分别点击IO口进行以下操作:

右键GPIO_0选择Make External

右键SPI_0 选择Make External

6)source→Design Source ,右键我们创建的BLOCK工程,点击create HDL wrapper,打包BLOCK文件并生成.v代码

这时候我们可以看到软件的TOP层 的verilog文件,打开后观察管脚信号,如下图

]

圈内的几个管脚是我们之前定义的SPI 管脚,和GPIO管脚, 其中

其中spi_0_io0为 SPI的MISO信号, spi_0_io1_io 为MOSI信号 ,SCK 为SPI时钟信号, SS,SS1,SS2分别为三组SPI 的SN信号

GPIO_0_0为刚才定义的三个GPIO (控制屏幕的背光, 复位 和D/C用)

7) 精简屏幕没用到的SPI管脚(屏幕多数SPI管脚并没有使用到)

屏幕实际只用到了 SPI的数据SDA和时钟SCL两个信号, 所以SPI 我们只需要引出 MOSI (SDA) 和 SCK(SCL)即可

删除如下框图中的信号的位置 (输入输出定义,以及模块的信号定义两处) ,最后一行保留 “);” 容易倍误删

同样也需要删除两个内部 buf(否则 外部管脚不调用会报错)

8) 点击绿色箭头RUN 对代码进行编译

9) 点击RTL 中的SCHEMATIC , 并选择右边出现的 IO Ports 来增加SPI的管脚定义(这一步也可以在约束文件中定义, 可看之前的例子)

修改GPIO 管脚定义 和SPI管脚定义,如下图所示 ,修改后保存(如弹出 窗口需要,则在窗口中输入约束文件名,然后保存)

BL T20

D/C R18

SCL R19

SDA P20

RES N17

10) 生成bit文件 :按下Generate Bitstream 完成综合以及生成bit文件

5.SDK程序编写

1)File→Export→Export hardware…,在弹出的对话框中勾选“include bitstream”,点击“OK”确认,如下图所示。

2)File→Lauch SDK,在弹出的对话框中,保存默认,点击“OK”,如下图所示。

系统将自动打开SDK开发环境

3)新建一个工程 file→new→Application Project,来新建一个“Application Project”,如下图所示。

4)在新建工程名中输入自己的工程名称,点击NEXT

5)选择一个空工程,点击完成

6) 在空工程中创建我们自己的代码

展开我们创建的工程,在src目录上右键,选择New->Source File,如下图所示:

在弹出的窗口中创建一个main.c文件

6)书写自己的代码

6.1小贴士:

在初次写PS部分的代码时,如GPIO代码 和SPI代码,不知道怎么写的情况下,其实可以通过打开 BSP工程下的 system.mss文件, 然后在右边导入需要的参考例程, 然后将例程中自己需要的部分(如GPIO初始化,或者SPI的写和读代码COPY到自己工程的main函数中)

6.2定义GPIO部分

下面进入正题, 我们要点亮屏幕, 屏幕这里用了3个GPIO 都是用了ZYNQ PS端的EMIO资源, EMIO的GPIO资源是从54开始的, 所以我们根据FPGA管脚约束中的 GPIO 0-3 分别对应 EMIO的54-55-56,如下图对GPIO进行定义

#define EMIO_LCD_BL 54 #define EMIO_LCD_CD 55 #define EMIO_LCD_RES 56

接下来 简单介绍下GPIO的调用

a. GPIO的初始化

如下图所示,需要先定义一个XgpioPs 的结构体指针, 这个指针的内容包括gpio分配的ID和基地址,这些信息通常在xparameter.h头文件中, 可以通过使用XGpioPs_LookupConfig函数,它能够在配置信息中找到对应ID的配置信息

ConfigPtr=XGpioPs_LookupConfig(GPIO_DEVICE_ID); 就相当于把获得到的信息赋予给之前定义的ConfigPtr结构体

最终再用 XGpioPs_CfgInitialize 函数来初始化GPIO

void Lcd_Gpio_Init(void){ XGpioPs_Config *ConfigPtr; ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID); XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr); XGpioPs_SetDirectionPin(&Gpio, EMIO_LCD_BL, 1); XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LCD_BL, 1); XGpioPs_SetDirectionPin(&Gpio, EMIO_LCD_CD, 1); XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LCD_CD, 1); XGpioPs_SetDirectionPin(&Gpio, EMIO_LCD_RES, 1); XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LCD_RES, 1); XGpioPs_WritePin(&Gpio, EMIO_LCD_BL, 0); XGpioPs_WritePin(&Gpio, EMIO_LCD_CD, 0); XGpioPs_WritePin(&Gpio, EMIO_LCD_RES, 0); }

b.GPIO的使用 (EMIO_LCD_BL为之前定义的 EMIO 54 的管脚)

XGpioPs_SetDirectionPin(&Gpio, EMIO_LCD_BL, 1);//设置成输出模式 XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LCD_BL, 1);//打开输出使能

XGpioPs_WritePin(&Gpio, EMIO_LCD_BL, 0);// 0置低 XGpioPs_WritePin(&Gpio, EMIO_LCD_BL, 1);// 1拉高

6.3 SPI部分的定义 ,和GPIO类似 SPI也有初始化和使用两部分

a.SPI的初始化 和GPIO类似,也是定义了一个结构体,也同样通过 LookupConfig 来获得对应的硬件配置信息, 最终用 XSpiPs_CfgInitialize来完成初始化,值得注意的是程序当中 XSpiPs_SetClkPrescaler 函数是用来为SPI功能提供主时钟的分频用的,可以根据自己需要的速率选择分频参数

void Lcd_Spi_Init(){ XSpiPs_Config *SpiConfig; SpiConfig = XSpiPs_LookupConfig(SPI_DEVICE_ID); XSpiPs_CfgInitialize(&SpiInstance, SpiConfig, SpiConfig->BaseAddress); XSpiPs_SetOptions(&SpiInstance, XSPIPS_MASTER_OPTION | XSPIPS_FORCE_SSELECT_OPTION); XSpiPs_SetClkPrescaler(&SpiInstance,XSPIPS_CLK_PRESCALE_128); }

b.传输 传输是用 XSpiPs_PolledTransfer 函数进行的, 其中 SendBufPtr 是发送的数据, RecvBufPtr 是接受的数据, ByteCount 可以定义我们的SPI 是8位 16位或者32位,下面是这个函数的定义

s32 XSpiPs_PolledTransfer(XSpiPs *InstancePtr, u8 *SendBufPtr,u8 *RecvBufPtr, u32 ByteCount)

7)上面简单介绍了PS部分的GPIO和,SPI功能,接下来在main.c中 完善屏幕的代码, 将下面的完整代码复制到工程中

#include "xparameters.h" #include "xgpiops.h" #include "xstatus.h" #include "xplatform_info.h" #include "xspips.h" #include #define WHITE 0xFFFF #define BLACK 0x0000 #define BLUE 0x001F #define BRED 0XF81F #define GRED 0XFFE0 #define GBLUE 0X07FF #define RED 0xF800 #define MAGENTA 0xF81F #define GREEN 0x07E0 #define CYAN 0x7FFF #define YELLOW 0xFFE0 #define BROWN 0XBC40 #define BRRED 0XFC07 #define GRAY 0X8430 #define EMIO_LCD_BL 54 #define EMIO_LCD_CD 55 #define EMIO_LCD_RES 56 #define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID #define SPI_DEVICE_ID XPAR_XSPIPS_0_DEVICE_ID XGpioPs Gpio; /* The driver instance for GPIO Device. */ static XSpiPs SpiInstance; void Lcd_Gpio_Init(void){ XGpioPs_Config *ConfigPtr; ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID); XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr); XGpioPs_SetDirectionPin(&Gpio, EMIO_LCD_BL, 1); XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LCD_BL, 1); XGpioPs_SetDirectionPin(&Gpio, EMIO_LCD_CD, 1); XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LCD_CD, 1); XGpioPs_SetDirectionPin(&Gpio, EMIO_LCD_RES, 1); XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LCD_RES, 1); XGpioPs_WritePin(&Gpio, EMIO_LCD_BL, 0); XGpioPs_WritePin(&Gpio, EMIO_LCD_CD, 0); XGpioPs_WritePin(&Gpio, EMIO_LCD_RES, 0); } void Lcd_Spi_Init(){ XSpiPs_Config *SpiConfig; SpiConfig = XSpiPs_LookupConfig(SPI_DEVICE_ID); XSpiPs_CfgInitialize(&SpiInstance, SpiConfig, SpiConfig->BaseAddress); XSpiPs_SetOptions(&SpiInstance, XSPIPS_MASTER_OPTION| XSPIPS_CLK_ACTIVE_LOW_OPTION| XSPIPS_FORCE_SSELECT_OPTION); XSpiPs_SetClkPrescaler(&SpiInstance, XSPIPS_CLK_PRESCALE_64); } void delay(unsigned char i){ volatile int Delay; volatile int k; for(k=0;k>8; spi_dat[1]=dat; XSpiPs_PolledTransfer(&SpiInstance, spi_dat, NULL, 2); } void Address_set(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2) { LCD_WR_REG(0x2a); LCD_WR_DATA8(x1>>8); LCD_WR_DATA8(x1); LCD_WR_DATA8(x2>>8); LCD_WR_DATA8(x2); LCD_WR_REG(0x2b); LCD_WR_DATA8(y1>>8); LCD_WR_DATA8(y1); LCD_WR_DATA8(y2>>8); LCD_WR_DATA8(y2); LCD_WR_REG(0x2C); } void LCD_Test() { unsigned int i,j; Address_set(0,0,240-1,240-1); for(i=0;i=0&&i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3